package cn.gjsm.utils;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.SneakyThrows;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 采用jackson，增加更多的工具功能。
 * @author zhaixiaobin
 */
public class JSONUtil {
    private JSONUtil() {}

    /**
     * 将对象序列化成json字符串
     * @param object javaBean
     * @return jsonString json字符串
     */
    public static String toJson(Object object) {
        try {
            return getInstance().writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 格式化Json Object
     */
    public static String toFormatedJson(Object object) {
    	ObjectWriter writer = getInstance().writerWithDefaultPrettyPrinter();
        try {
            return writer.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 格式化Json String
     *
     * @param stringData
     * @return
     */
    public static String toFormatedJsonStr(String stringData) {
        ObjectNode object = parse(stringData);

        ObjectWriter writer = getInstance().writerWithDefaultPrettyPrinter();
        try {
            return writer.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Json String to Java bean.
     * @param jsonString jsonString
     * @param valueType class
     * @param <T> T 泛型标记
     * @return Bean
     */
    public static <T> T parse(String jsonString, Class<T> valueType) {
        try {
            return getInstance().readValue(jsonString, valueType);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *将json数组转化为Java List对象。举例：
     *
     *    @Data
     *    static class A {
     * 		String a;
     * 		String b;
     *    }
     *
     *    @Test
     *    public void testJsonArray(){
     * 		List<A> list = JSONUtil.parseArray("[{'a': '1', 'b' : '1'},{'a': '2', 'b' : 2}]", A.class);
     * 		log.info("{}", list);
     *    }
     *
     * @param jsonString
     * @param valueType
     * @param <T>
     * @return
     */
    @SneakyThrows
    public static <T> List<T> parseArray(String jsonString, Class<T> valueType) {
        CollectionType javaType = mapper.getTypeFactory().constructCollectionType(List.class, valueType);
        return getInstance().readValue(jsonString, javaType);
    }

    /**
     * Json string to ObjectNode object.
     *
     * @param jsonString
     * @param <T>
     * @return
     */
    public static <T> ObjectNode parseNode(String jsonString) {
        try {
            return getInstance().readValue(jsonString, ObjectNode.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Json array string to ArrayNode object.
     * @param jsonString
     * @param <T>
     * @return
     */
	public static <T> ArrayNode parseArray(String jsonString) {
		try {
			return getInstance().readValue(jsonString, ArrayNode.class);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

    /**
     *  Json to Java bean.
     * @param jsonNode
     * @param valueType
     * @param <T>
     * @return
     */
    public static <T> T parse(JsonNode jsonNode, Class<T> valueType) {
        return mapper.convertValue(jsonNode, valueType);
    }

    /**
     * Java bean to ObjectNode
     * @param bean
     * @param <T>
     * @return
     */
    public static <T> ObjectNode parse(T bean){

    	return Objects.isNull(bean)? getObject() : mapper.convertValue(bean, ObjectNode.class);
    }

    /**
     * Java bean list to ArrayNode
     * @param list
     * @param <T>
     * @return
     */
    public static <T> ArrayNode parse(Collection<T> list){
    	return Objects.isNull(list)? getArray() : mapper.convertValue(list, ArrayNode.class);
    }

    private final static JacksonObjectMapper mapper;
    static {
    	mapper = new JacksonObjectMapper();
    }
    public static ObjectMapper getInstance() {
        return mapper;
    }

    /**
     *
     * @return 空Json对象  {}。
     */
    public static ObjectNode getObject() {
        return mapper.createObjectNode();
    }

    /**
     * @return 空Json数组  []。
     */
    public static ArrayNode getArray() {
        return mapper.createArrayNode();
    }

//    public static String convertXmlToJson(String xml) {
//        XmlMapper xmlMapper = new XmlMapper();
//        ObjectMapper objectMapper = new ObjectMapper();
//
//        StringWriter w = new StringWriter();
//        try {
//            JsonParser jp = xmlMapper.getFactory().createParser(xml);
//            JsonGenerator jg = objectMapper.getFactory().createGenerator(w);
//            while (jp.nextToken() != null) {
//                jg.copyCurrentEvent(jp);
//            }
//            jp.close();
//            jg.close();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return w.toString();
//    }

//    public static String convertXmlToJson(File xml) throws IOException {
//        FileReader reader = new FileReader(xml);
//
//        //Reader initialReader = CharSource.wrap("With Google Guava").openStream();
//        String targetString = CharStreams.toString(reader);
//        reader.close();
//        return convertXmlToJson(targetString);
//    }

    private static class JacksonObjectMapper extends ObjectMapper {
        private static final long serialVersionUID = 4288193147502386170L;
        private static final Locale CHINA = Locale.CHINA;

        public JacksonObjectMapper() {
            JavaTimeModule timeModule = new JavaTimeModule();
            timeModule.addDeserializer(LocalDate.class,
                    new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            timeModule.addDeserializer(LocalDateTime.class,
                    new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            timeModule.addSerializer(LocalDate.class,
                    new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            timeModule.addSerializer(LocalDateTime.class,
                    new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

            this.setLocale(CHINA)
            .setTimeZone(TimeZone.getTimeZone("GMT+8"))
            .setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", CHINA))
            .registerModule(timeModule)
            .registerModule(new Jdk8Module())

            .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false)
            .setSerializerFactory(this.getSerializerFactory().withSerializerModifier(new CustomBeanSerializerModifier()))
            //.setSerializationInclusion(Include.NON_NULL)
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        }
    }

    private static class NumberNullSerializer extends JsonSerializer<Object> {
		@Override
		public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
			gen.writeNumber(0);
		}
    };

    private static class CustomBeanSerializerModifier extends BeanSerializerModifier {
        private JsonSerializer<Object> numberNullSerializer = new NumberNullSerializer();

        @Override
        public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
                                                         List<BeanPropertyWriter> beanProperties) {
            for (int i = 0; i < beanProperties.size(); i++) {
                BeanPropertyWriter writer = beanProperties.get(i);

                if (isDecimalType(writer)) {
                	writer.assignNullSerializer(numberNullSerializer);
                }
            }
            return beanProperties;
        }

        protected boolean isDecimalType(BeanPropertyWriter writer) {
            JavaType type = writer.getType();
            @SuppressWarnings("rawtypes")
            Class clazz = type.getRawClass();

            return clazz.equals(Long.class) || clazz.equals(Integer.class) || clazz.equals(Short.class);
        }
    }

}
